package com.ydl.hostmonitor.service.impl;

import com.ydl.hostmonitor.service.SystemInfoService;
import org.springframework.stereotype.Service;
import oshi.SystemInfo;
import oshi.hardware.CentralProcessor;
import oshi.hardware.GlobalMemory;
import oshi.hardware.HardwareAbstractionLayer;
import oshi.software.os.FileSystem;
import oshi.software.os.OSFileStore;
import oshi.software.os.OperatingSystem;
import oshi.util.FormatUtil;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
public class SystemInfoServiceImpl implements SystemInfoService {

    private final SystemInfo systemInfo;
    private final HardwareAbstractionLayer hardware;
    private final OperatingSystem os;

    public SystemInfoServiceImpl() {
        this.systemInfo = new SystemInfo();
        this.hardware = systemInfo.getHardware();
        this.os = systemInfo.getOperatingSystem();
    }

    @Override
    public Map<String, Object> getCpuInfo() {
        Map<String, Object> cpuInfo = new HashMap<>();
        CentralProcessor processor = hardware.getProcessor();

        // CPU基本信息
        cpuInfo.put("name", processor.getProcessorIdentifier().getName());
        cpuInfo.put("physicalCores", processor.getPhysicalProcessorCount());
        cpuInfo.put("logicalCores", processor.getLogicalProcessorCount());
        cpuInfo.put("vendor", processor.getProcessorIdentifier().getVendor());
        cpuInfo.put("stepping", processor.getProcessorIdentifier().getStepping());
        cpuInfo.put("model", processor.getProcessorIdentifier().getModel());

        // CPU使用率
        long[] prevTicks = processor.getSystemCpuLoadTicks();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        long[] ticks = processor.getSystemCpuLoadTicks();

        long user = ticks[CentralProcessor.TickType.USER.getIndex()] - prevTicks[CentralProcessor.TickType.USER.getIndex()];
        long nice = ticks[CentralProcessor.TickType.NICE.getIndex()] - prevTicks[CentralProcessor.TickType.NICE.getIndex()];
        long sys = ticks[CentralProcessor.TickType.SYSTEM.getIndex()] - prevTicks[CentralProcessor.TickType.SYSTEM.getIndex()];
        long idle = ticks[CentralProcessor.TickType.IDLE.getIndex()] - prevTicks[CentralProcessor.TickType.IDLE.getIndex()];
        long iowait = ticks[CentralProcessor.TickType.IOWAIT.getIndex()] - prevTicks[CentralProcessor.TickType.IOWAIT.getIndex()];
        long irq = ticks[CentralProcessor.TickType.IRQ.getIndex()] - prevTicks[CentralProcessor.TickType.IRQ.getIndex()];
        long softirq = ticks[CentralProcessor.TickType.SOFTIRQ.getIndex()] - prevTicks[CentralProcessor.TickType.SOFTIRQ.getIndex()];
        long totalCpu = user + nice + sys + idle + iowait + irq + softirq;

        cpuInfo.put("usage", (100d - (idle * 100d / totalCpu)));
        cpuInfo.put("userUsage", (user * 100d / totalCpu));
        cpuInfo.put("systemUsage", (sys * 100d / totalCpu));

        return cpuInfo;
    }

    @Override
    public Map<String, Object> getMemoryInfo() {
        Map<String, Object> memoryInfo = new HashMap<>();
        GlobalMemory memory = hardware.getMemory();

        memoryInfo.put("total", memory.getTotal());
        memoryInfo.put("available", memory.getAvailable());
        memoryInfo.put("used", memory.getTotal() - memory.getAvailable());
        memoryInfo.put("usedPercent", 100 - (memory.getAvailable() * 100d / memory.getTotal()));
        memoryInfo.put("totalFormatted", FormatUtil.formatBytes(memory.getTotal()));
        memoryInfo.put("availableFormatted", FormatUtil.formatBytes(memory.getAvailable()));
        memoryInfo.put("usedFormatted", FormatUtil.formatBytes(memory.getTotal() - memory.getAvailable()));

        return memoryInfo;
    }

    @Override
    public Map<String, Object> getDiskInfo() {
        Map<String, Object> diskInfo = new HashMap<>();
        FileSystem fileSystem = os.getFileSystem();

        long totalSpace = 0;
        long usedSpace = 0;
        long freeSpace = 0;

        for (OSFileStore store : fileSystem.getFileStores()) {
            totalSpace += store.getTotalSpace();
            usedSpace += store.getTotalSpace() - store.getUsableSpace();
            freeSpace += store.getUsableSpace();
        }

        diskInfo.put("total", totalSpace);
        diskInfo.put("used", usedSpace);
        diskInfo.put("free", freeSpace);
        diskInfo.put("usedPercent", totalSpace > 0 ? (usedSpace * 100d / totalSpace) : 0);
        diskInfo.put("totalFormatted", FormatUtil.formatBytes(totalSpace));
        diskInfo.put("usedFormatted", FormatUtil.formatBytes(usedSpace));
        diskInfo.put("freeFormatted", FormatUtil.formatBytes(freeSpace));

        return diskInfo;
    }

    @Override
    public Map<String, Object> getSystemOverview() {
        Map<String, Object> overview = new HashMap<>();

        overview.put("cpu", getCpuInfo());
        overview.put("memory", getMemoryInfo());
        overview.put("disk", getDiskInfo());
        overview.put("os", os.toString());
        overview.put("uptime", FormatUtil.formatElapsedSecs(os.getSystemUptime()));

        return overview;
    }
}